﻿using Grow.Json;
using Grow.Module.Service;
using Grow.Utils.Exceptions;

namespace Grow.Domain.EventBus.Cap.PublisherDomainEvents
{
    public class DomainEventPublisher : IDomainEventPublisher
    {
        private readonly List<PublisherDomainEventDto> _publisherDomainEventDtos;
        private readonly IServiceContext _serviceContext;
        private readonly DomainEventBusOptions _domainEventBusOptions;
        private readonly IJsonContext _jsonContext;
        private readonly IPublisherDomainEventDomainRepository _publisherDomainEventDomainRepository;
        private readonly IPublisherDomainEventHandler _publisherHandler;
        public DomainEventPublisher(IServiceContext serviceContext)
        {
            _publisherDomainEventDtos = new();
            _serviceContext = serviceContext;
            _domainEventBusOptions = _serviceContext.GetOptions<DomainEventBusOptions>();
            _jsonContext = _serviceContext.GetRequiredService<IJsonContext>();
            _publisherDomainEventDomainRepository = _serviceContext.GetRequiredService<IPublisherDomainEventDomainRepository>();
            _publisherHandler = _serviceContext.GetRequiredService<IPublisherDomainEventHandler>();
        }



        public async Task SaveAsync(IDomainEvent domainEvent, CancellationToken cancellationToken = default)
        {
            DateTimeOffset sendAt = DateTimeOffset.Now;
            DomainEventOptions domainEventOptions = _serviceContext.GetOptions<DomainEventOptions>(typeof(IDomainEvent).FullName);

            string? eventDataJson = await _jsonContext.SerializeAsync(domainEvent, cancellationToken);
            if (eventDataJson is null)
            {
                throw new GrowCheckException($"eventDataId  {domainEvent.Id} eventDataJson is null");
            }

            PublisherDomainEvent publisherDomainEvent = new(
                domainEvent.Id,
                _domainEventBusOptions.EnvironmentName,
                domainEventOptions.EventName,
                eventDataJson,
                PublisherDomainEventStatus.Wait,
                0,
                sendAt,
                null,
                null);

            await _publisherDomainEventDomainRepository.AddAsync(publisherDomainEvent, cancellationToken);

            _publisherDomainEventDtos.Add(new PublisherDomainEventDto(
                domainEvent.Id,
                domainEventOptions.EventName,
                eventDataJson,
                PublisherDomainEventStatus.Wait,
                0,
                sendAt,
                null,
                null)
                );
        }

        public Task PublisherAsync(CancellationToken cancellationToken = default)
        {
            _ = Task.Factory.StartNew(async () =>
            {
                foreach (PublisherDomainEventDto publisherDomainEventDto in _publisherDomainEventDtos)
                {
                    await _publisherHandler.PublisherAsync(publisherDomainEventDto, cancellationToken);
                }
                _publisherDomainEventDtos.Clear();
            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            return Task.CompletedTask;
        }
    }
}
